#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <sys/un.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <iostream>
#include <fstream>
#include "hw_app_log.h"
#include "hw_rest_api.h"
#include "md5.h"
#include "json.hpp"
using json = nlohmann::json;

#define MAX_LENGTH 1024 * 1024 * 3
#define SEND_LENGTH 1024
#define APP_ERR_CODE -1
#define APP_OK_CODE 0

#define UNAUTHORIZED_CODE 401
#define ERRORGATEWAY_CODE 502
#define NOFOUND_CODE 404
#define SUCCESS_CODE 200

namespace gc {
// 判断字符串b是否被包含在字符串a中
static int string_isContained(std::string a, std::string b)
{
    std::string::size_type idx;
    idx = a.find(b);
    if (idx == std::string::npos) {
        return 0;
    } else {
        return 1;
    }
}

// 解析http报文并将body数据转换为json格式
static int httpRspToJson(std::string &rsp_s, json &j_body)
{
    LOG_DEBUG("rsp_s: %s", rsp_s.c_str());
    int pos = rsp_s.find_last_of("\r\n\r\n");
    std::string rsp_body_s = rsp_s.substr(pos + 1, rsp_s.size() - pos);
    LOG_DEBUG("rest api resp: %s", rsp_body_s.c_str());
    try {
        j_body = json::parse(rsp_body_s);
        if (j_body.contains("ResponseStatus")) {
            if (j_body.at("ResponseStatus").contains("StatusCode") && j_body.at("ResponseStatus")["StatusCode"] != 0) {
                LOG_ERROR("rest api resp: %s", rsp_body_s.c_str());
                return APP_ERR_CODE;
            }
        }
    } catch (...) {
        return APP_ERR_CODE;
    }
    return APP_OK_CODE;
}

CameraConfig::CameraConfig(std::string &ip_s)
{
    cam_ip = ip_s;
}

CameraConfig::~CameraConfig()
{
    close(sockfd);
}

int CameraConfig::Connect()
{
    int byte, len, result;
    LOG_INFO("the client is in");
    struct sockaddr_un address;
    // 创建socket，指定通信协议为AF_UNIX,数据方式SOCK_STREAM
    if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
        LOG_ERROR("socket init fail");
        return APP_ERR_CODE;
    }

    // 配置server_address
    address.sun_family = AF_UNIX;
    // 将地址绑定到nginx监听的域套接字/tmp/http_proxy_connect.socket
    strcpy(address.sun_path, "/tmp/http_proxy_connect.socket");
    len = sizeof(address);
    // printf("The client begin to connect service\n");
    // 建立连接
    result = connect(sockfd, (struct sockaddr *)&address, len);
    // printf("The connect result from service is %d, %s\n",result, strerror(errno));
    if (result == -1) {
        LOG_ERROR("proxy connect fail, ensure the server is up\n");
        return APP_ERR_CODE;
    }
    /*---------------------------------------开始CONNECT连接----------------------------------*/
    // 要发给域套接字的请求信息
    char connectUrl[SEND_LENGTH];
    sprintf(connectUrl, "CONNECT %s:80 HTTP/1.1\r\nHost:%s:80\r\n\r\n", cam_ip.c_str(), cam_ip.c_str());
    // 存放发送的信息
    char send_buf_connect[SEND_LENGTH];
    // 存放收到的信息
    char ch_recv_connect[MAX_LENGTH];
    // 用sprintf事先把消息写到send_buf_connect
    sprintf(send_buf_connect, connectUrl);
    // printf("The send message is %s and length is %d\n",send_buf_connect,strlen(send_buf_connect));
    // 发送请求数据
    if ((byte = write(sockfd, send_buf_connect, strlen(send_buf_connect))) == -1) {
        LOG_ERROR("请求数据发送失败, ip=%s", cam_ip.c_str());
        return APP_ERR_CODE;
    }
    // 接收返回数据
    if ((byte = read(sockfd, &ch_recv_connect, MAX_LENGTH)) == -1) {
        LOG_ERROR("响应数据接收失败, ip=%s", cam_ip.c_str());
        return APP_ERR_CODE;
    }
    // printf("receive from server data is: %s\n",ch_recv_connect);

    /*---------------------------------------CONNECT 连接成功----------------------------------*/
    return APP_OK_CODE;
}

int CameraConfig::SendRequest(std::string &url_path, std::string &rsp_content)
{
    /*---------------------------------------开始发送get请求----------------------------------*/
    // 要发给代理的请求信息
    int byte;
    char getUrl[SEND_LENGTH];
    sprintf(getUrl, "GET %s HTTP/1.1\r\nProxy-Connection:keep-alive\r\nHost:%s:80\r\n\r\n", url_path.c_str(),
            cam_ip.c_str());
    // 存放发送的信息
    char send_buf_get[SEND_LENGTH];
    // 存放收到的信息
    char ch_recv_get[MAX_LENGTH];
    // 用sprintf事先把消息写到send_buf
    sprintf(send_buf_get, getUrl);
    // printf("The send message is %s and length is %d\n",send_buf_get,strlen(send_buf_get));
    if ((byte = write(sockfd, send_buf_get, strlen(send_buf_get))) == -1) {
        LOG_ERROR("请求数据发送失败, ip=%s", cam_ip.c_str());
        return APP_ERR_CODE;
    }
    if ((byte = read(sockfd, &ch_recv_get, MAX_LENGTH)) == -1) {
        LOG_ERROR("响应数据接收失败, ip=%s", cam_ip.c_str());
        return APP_ERR_CODE;
    }
    // printf("The result from service is: %s\n",ch_recv_get);
    /*---------------------------------------get请求结束----------------------------------*/

    std::string ch_recv_get_s = ch_recv_get;

    if (string_isContained(ch_recv_get_s, std::to_string(SUCCESS_CODE))) {
        LOG_DEBUG("正确响应(200), ip=%s", cam_ip.c_str());
        rsp_content = ch_recv_get_s;
    } else if (string_isContained(ch_recv_get_s, std::to_string(UNAUTHORIZED_CODE))) {
        // 返回401 进行Digest Auth
        // 定位nonce，并截取
        LOG_DEBUG("正在鉴权(401), ip=%s", cam_ip.c_str());

        int nonce_start_pos = ch_recv_get_s.find("nonce=\"");
        std::string nonce_s = ch_recv_get_s.substr(nonce_start_pos + sizeof("nonce=\"") - 1, 32);

        std::string A1 = md5(username + ":Huawei.com:" + password);
        std::string A2 = md5("GET:" + url_path);
        std::string response = md5(A1 + ":" + nonce_s + ":" + A2);

        // std::cout << "A1: " << A1 << "\n";
        // std::cout << "A2: " << A2 << "\n";
        // std::cout << "response: " << response << "\n";

        // 构建Authorization字段
        char Authorization[SEND_LENGTH];
        sprintf(
            Authorization,
            "Authorization: Digest username=\"%s\", realm=\"Huawei.com\", nonce=\"%s\", uri=\"%s\", algorithm=\"MD5\", response=\"%s\"",
            username.c_str(), nonce_s.c_str(), url_path.c_str(), response.c_str());

        char resend_s[SEND_LENGTH];
        sprintf(resend_s, "GET %s HTTP/1.1\r\nProxy-Connection:keep-alive\r\nHost:%s:80\r\n%s\r\n\r\n",
                url_path.c_str(), cam_ip.c_str(), Authorization);
        // printf("The resend message is %s and length is %d\n",resend_s,strlen(resend_s));

        if ((byte = write(sockfd, resend_s, strlen(resend_s))) == -1) {
            LOG_ERROR("请求数据发送失败, ip=%s", cam_ip.c_str());
            return APP_ERR_CODE;
        }

        char recv_s[MAX_LENGTH];
        if ((byte = read(sockfd, &recv_s, MAX_LENGTH)) == -1) {
            LOG_ERROR("响应数据接收失败, ip=%s", cam_ip.c_str());
            return APP_ERR_CODE;
        }

        rsp_content = recv_s;
        // printf("The result from service is: %s\n",recv_s);
        memset(recv_s, '\0', sizeof(recv_s));
    } else if (string_isContained(ch_recv_get_s, std::to_string(ERRORGATEWAY_CODE))) {
        LOG_ERROR("错误的网关(502), url_path=%s ip=%s", url_path.c_str(), cam_ip.c_str());
        return APP_ERR_CODE;
    } else if (string_isContained(ch_recv_get_s, std::to_string(NOFOUND_CODE))) {
        LOG_ERROR("接口不存在(404), url_path=%s ip=%s", url_path.c_str(), cam_ip.c_str());
        return APP_ERR_CODE;
    } else {
        LOG_ERROR("未知错误：%s, url_path=%s ip=%s", ch_recv_get_s.c_str(), url_path.c_str(), cam_ip.c_str());
        return APP_ERR_CODE;
    }

    return APP_OK_CODE;
}

int CameraConfig::getUUID(std::string &uuid_s)
{
    /*
        params:
            uuid_s: camera uuid
    */
    int ret;
    json parseData;
    std::string url_path = "/SDCAPI/V1.0/CnsPaas/ChnQury";
    std::string rsp_content;
    ret = SendRequest(url_path, rsp_content);

    if (ret < 0) {
        LOG_ERROR("请求%s失败, ip=%s", url_path.c_str(), cam_ip.c_str());
        return APP_ERR_CODE;
    } else {
        ret = httpRspToJson(rsp_content, parseData);
        if (ret < 0) {
            LOG_ERROR("json parse fail, ip=%s, rsp_content=%s", cam_ip.c_str(), rsp_content.c_str());
            return APP_ERR_CODE;
        }
        uuid_s = parseData["CnsChnParam"][0]["uuid"];
    }

    return APP_OK_CODE;
}

int CameraConfig::getDevType(std::string &devType_s)
{
    /*
        params:
            ip_s: camera deType
    */
    int ret;
    json parseData;
    std::string url_path = "/SDCAPI/V1.0/MiscIaas/System?ChannelId=101";
    std::string rsp_content;
    ret = SendRequest(url_path, rsp_content);

    if (ret < 0) {
        LOG_ERROR("请求%s失败, ip=%s", url_path.c_str(), cam_ip.c_str());
        return APP_ERR_CODE;
    } else {
        ret = httpRspToJson(rsp_content, parseData);
        if (ret < 0) {
            LOG_ERROR("json parse fail, ip=%s, rsp_content=%s", cam_ip.c_str(), rsp_content.c_str());
            return APP_ERR_CODE;
        }
        devType_s = parseData["devType"];
    }
    return APP_OK_CODE;
}

int CameraConfig::getIsOpenCorridorMode(bool &res)
{
    /*
        params:
            res: isSupportCorridorMode or not
    */
    int ret;
    json parseData;
    std::string url_path = "/SDCAPI/V1.0/VideoIaas/ChannelsCfg?ChannelId=101";
    std::string rsp_content;
    ret = SendRequest(url_path, rsp_content);

    if (ret < 0) {
        LOG_ERROR("请求%s失败, ip=%s", url_path.c_str(), cam_ip.c_str());
        return APP_ERR_CODE;
    } else {
        ret = httpRspToJson(rsp_content, parseData);
        if (ret < 0) {
            LOG_ERROR("json parse fail, ip=%s, rsp_content=%s", cam_ip.c_str(), rsp_content.c_str());
            return APP_ERR_CODE;
        }
        res = parseData["corridorModeOpen"];
    }
    return APP_OK_CODE;
}

int CameraConfig::get_camera_config_byRestApi()
{
    if (Connect() > -1) {
        // 通过代理与外界交互
        hw_rest_api restRsp;
        // 获取uuid 后续版本uuid逐渐替代channelID
        if (getUUID(restRsp.uuid) < 0) {
            LOG_ERROR("uuid查询失败, ip=%s", cam_ip.c_str());
        }
        GC_CAMERA_MODEL model = GC_CAMERA_MODEL::MODEL_UNKNOWN;
        GC_CAMERA_MODE mode = GC_CAMERA_MODE::MODE_WK;         // 默认微卡模式
        // 获取设备型号
        if (getDevType(restRsp.dev_type) == APP_OK_CODE) {
            // TODO 此处待完善，目前方式扩展新型号不方便
            if (string_isContained(restRsp.dev_type, "M2241")) {
                model = GC_CAMERA_MODEL::MODEL_M2241;
            } else if (string_isContained(restRsp.dev_type, "M2281")) {
                model = GC_CAMERA_MODEL::MODEL_M2281;
            } else if (string_isContained(restRsp.dev_type, "X2241")) {
                model = GC_CAMERA_MODEL::MODEL_X2241;
            } else if (string_isContained(restRsp.dev_type, "M2141")) {
                model = GC_CAMERA_MODEL::MODEL_M2141;
            } else if (string_isContained(restRsp.dev_type, "C6650")) {
                model = GC_CAMERA_MODEL::MODEL_C6650;
            } else if (string_isContained(restRsp.dev_type, "M6741")) {
                model = GC_CAMERA_MODEL::MODEL_M6741;
            } else if (string_isContained(restRsp.dev_type, "C2150")) {
                model = GC_CAMERA_MODEL::MODEL_C2150;
            } else if (string_isContained(restRsp.dev_type, "C6620")) {
                model = GC_CAMERA_MODEL::MODEL_C6620;
            }
        } else {
            LOG_ERROR("设备类型查询失败, ip=%s", cam_ip.c_str());
        }
        // 获取是否开启走廊模式
        if (getIsOpenCorridorMode(restRsp.enable_corridorMode) == APP_OK_CODE) {
            mode = restRsp.enable_corridorMode ? GC_CAMERA_MODE::MODE_ZL : GC_CAMERA_MODE::MODE_WK;
        } else {
            LOG_ERROR("走廊模式查询失败, ip=%s", cam_ip.c_str());
        }
        std::map<std::string, GC_CAMERA_CONFIG>::iterator iter = gc_app_config.camera_config_map.begin();
        while (iter != gc_app_config.camera_config_map.end()) {
            iter->second.mode = mode;
            iter->second.model = model;
            iter++;
        }
    } else {
        LOG_ERROR("socket connet fail, ip=%s", cam_ip.c_str());
        return APP_ERR_CODE;
    }
    return APP_OK_CODE;
}
}  // namespace gc